/* 
 * Copyright (C) 2006-2011 Crise, crise<at>mail.berlios.de
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdinc.h"
#include "HttpManager.h"

#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/for_each.hpp>

#include "LogManager.h"
#include "File.h"
#include "Pointer.h"
#include "HttpConnection.h"

namespace dcpp {

using boost::adaptors::map_values;
using boost::range::for_each;

// This derivative of HttpConnection uses callbacks instead of listener/speaker mechanics
class HttpDownload : public HttpConnection
{
public:
	HttpDownload(const HttpManager::CallBack& func)
		: cbFunc(func), dlBuffer(Util::emptyString), file(NULL), HttpConnection(NULL) { setOutput(new StringOutputStream(dlBuffer)); }

	HttpDownload(const HttpManager::CallBack& func, const string& fileName, File* f)
		: cbFunc(func), dlBuffer(fileName), file(f), HttpConnection(NULL) { setOutput(f); }

	~HttpDownload() { }
private:
	HttpManager::CallBack cbFunc;
	string dlBuffer;
	File* file;

	// override the default notification behaviour
	void notifyAborted() noexcept {
		if(file)
			file->close();

		cbFunc(this, getURL(), HttpManager::HTTP_FAILED | HttpManager::HTTP_ABORT);
		HttpManager::getInstance()->putDownload(this);
	}

	void notifyCompleted() noexcept {
		if(file)
			file->close();

		cbFunc(this, dlBuffer, HttpManager::HTTP_COMPLETE | (isCoral() ? HttpManager::HTTP_CORAL : 0));
		HttpManager::getInstance()->putDownload(this);
	}

	void notifyFailed(const string& aError) noexcept {
		if(file)
			file->close();

		cbFunc(this, aError + " (" + getURL() + ")", HttpManager::HTTP_FAILED);
		HttpManager::getInstance()->putDownload(this);
	}

	void notifyRedirected(const string& /*aLocation*/) noexcept { }
};

HttpManager::~HttpManager() {
	Lock l(cs);
	for_each(active | map_values, DeleteFunction());

	TimerManager::getInstance()->removeListener(this);
}

void HttpManager::putDownload(HttpDownload* dl) noexcept {
	Lock l(cs);
	auto i = active.find(dl->getURL());
	if(i != active.end()) {
		active.erase(i);
		finished.push_back(dl);
	}
}

void HttpManager::on(TimerManagerListener::Second, uint64_t /*aTick*/) noexcept {
	if(!finished.empty()) {
		std::for_each(finished.begin(), finished.end(), DeleteFunction());
		finished.clear();
	}
}

void HttpManager::addDownload(const string& aUrl, const CallBack& cbFunc, bool allowCache, const string& aUserAgent) {
	Lock l(cs);

	HttpDownload* dl = new HttpDownload(cbFunc);
	if(!aUserAgent.empty()) dl->setUserAgent(aUserAgent);
	if(!allowCache) dl->setCoralizeState(HttpConnection::CST_NOCORALIZE);
	dl->downloadFile(aUrl);

	active[aUrl] = dl;
}

void HttpManager::addFileDownload(const string& aUrl, const string& aFile, const CallBack& cbFunc, bool allowCache, const string& aUserAgent) {
	Lock l(cs);

	File* f = NULL;
	try {
		File::ensureDirectory(aFile);
		f = new File(aFile, File::RW, File::OPEN | File::CREATE | File::TRUNCATE);
	} catch(const FileException& e) {
		if(f) delete f;
		LogManager::getInstance()->message("HTTP File download: " + e.getError(), LogManager::LOG_ERROR);
		return;
	}

	HttpDownload* dl = new HttpDownload(cbFunc, aFile, f);
	if(!aUserAgent.empty()) dl->setUserAgent(aUserAgent);
	if(!allowCache) dl->setCoralizeState(HttpConnection::CST_NOCORALIZE);
	dl->downloadFile(aUrl);

	active[aUrl] = dl;
}

void HttpManager::postForm(const string& aHandler, const StringMap& aData, const CallBack& cbFunc, const string& aUserAgent) {
	Lock l(cs);

	HttpDownload* dl = new HttpDownload(cbFunc);
	if(!aUserAgent.empty()) dl->setUserAgent(aUserAgent);
	dl->postData(aHandler, aData);

	active[aHandler] = dl;
}

} // namespace dcpp